package com.geezdata.cps.base.shiro;

import java.util.Collection;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

import org.apache.shiro.cache.Cache;
import org.apache.shiro.cache.CacheException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.data.redis.core.RedisTemplate;

import com.geezdata.cps.base.cache.redis.RedisKey;
import com.geezdata.cps.base.constant.ShiroConstants;

public class ShiroCache<K, V> implements Cache<K, V> {
	private static Logger logger = LoggerFactory.getLogger(ShiroCache.class);
	
	private String cacheKey;
	private RedisTemplate<K, V> redisTemplate;
	
	@SuppressWarnings({ "rawtypes", "unchecked" })
	public ShiroCache(String name, RedisTemplate redisTemplate) {
		this.cacheKey = RedisKey.SHIRO_CACHE_PREFIX + name + ":";
		this.redisTemplate = redisTemplate;
    }

	@Override
	public V get(K key) throws CacheException {
		logger.debug("RedisCache::get {}", key);
		K redisKey = getCacheKey(key);
		
		if (redisKey == null || !redisTemplate.hasKey(redisKey)) {
			return null;
		}
		
		redisTemplate.boundValueOps(redisKey).expire(ShiroConstants.EXPIRE_TIME, TimeUnit.SECONDS);
		V value = redisTemplate.boundValueOps(redisKey).get();

		return value;
	}

	@Override
	public V put(K key, V value) throws CacheException {
		logger.debug("RedisCache::put {}", key);
		V old = get(key);
		redisTemplate.boundValueOps(getCacheKey(key)).set(value); 
		redisTemplate.boundValueOps(getCacheKey(key)).expire(ShiroConstants.EXPIRE_TIME, TimeUnit.SECONDS);
        return old;
	}

	@Override
	public V remove(K key) throws CacheException {
		logger.debug("RedisCache::remove {}", key);
		V old = get(key);
		redisTemplate.delete(getCacheKey(key));
		return old;
	}

	@Override
	public void clear() throws CacheException {
		logger.debug("RedisCache::clear");
		redisTemplate.delete(keys());
	}

	@Override
	public int size() {
		return keys().size();
	}

	@Override
	public Set<K> keys() {
		return redisTemplate.keys(getCacheKey("*"));
	}

	@Override
	public Collection<V> values() {
		Set<K> keys = keys();
		if (keys == null || keys.isEmpty()) {
			return null;
		}
		
		return keys.stream()
				.map(key -> get(key))
				.collect(Collectors.toList());
	}
	
	@SuppressWarnings("unchecked")
	private K getCacheKey(Object k) {
        return (K)(this.cacheKey + k);
    }
}